﻿Partial Class Puzzle

    Function NakedCandidates() As Boolean
        Dim Progress As Boolean
        For Each Cell In Cells.Where(Function(c) c.Candidates.Count = 1)
            Progress = True
            Cell.SetValue(Cell.Candidates.First)
        Next
        Return Progress
    End Function

    Function SingleCandidates() As Boolean
        Dim Progress As Boolean = False
        For Each Result In (From g In Groups From c In g.PossibleCells Where c.Value.Count = 1 Select c).Distinct
            Result.Value.First.SetValue(Result.Key)
            Progress = True
        Next
        Return Progress
    End Function

    Function NakedPairs() As Boolean
        Dim Progress As Boolean = False
        For Each Result In ( _
                From g In Groups From c1 In g.Cells.Empties Where c1.Candidates.Count = 2 Let J = c1.CandidateStr _
                From c2 In g.Cells.Empties Where c2.Candidates.Count = 2 AndAlso c2 > c1 AndAlso J = c2.CandidateStr _
                From Cell In g.Cells Where (Cell(J(0)) OrElse Cell(J(1))) Where Not Cell.In(c1, c2)).ToList
            Log.Append("naked pair detected at " & Result.c1 & Result.c2 & " for " & Result.J & " eliminating from " & Result.Cell)
            Result.Cell.Eliminate(Result.J(0))
            Result.Cell.Eliminate(Result.J(1))
            Progress = True
        Next
        Return Progress
    End Function

    Function NakedTriples() As Boolean
        Dim Progress As Boolean = False
        For Each Result In ( _
                From g In Groups From c1 In g.Cells Where c1.Candidates.Count.Between(2, 3) _
                From c2 In g.Cells Where c2.Candidates.Count.Between(2, 3) AndAlso c2 > c1 Let J1 = c1.CandidateStr.Add(c2.CandidateStr) Where J1.Length = 3 _
                From c3 In g.Cells Where c3.Candidates.Count.Between(2, 3) AndAlso c3 > c2 Let J2 = c3.CandidateStr.Add(J1) Where J2.Length = 3 _
                From Cell In g.Cells Where (Cell(J2(0)) Or Cell(J2(1)) Or Cell(J2(2))) Where Not Cell.In(c1, c2, c3)).ToList
            Result.Cell.Eliminate(Result.J2(0))
            Result.Cell.Eliminate(Result.J2(1))
            Result.Cell.Eliminate(Result.J2(2))
            Progress = True
            Log.Append("naked triples detected at " & Result.c1 & Result.c2 & Result.c3 & " for " & Result.J2 & " eliminating from " & Result.Cell)
        Next
        Return Progress
    End Function

    Function HiddenPairs() As Boolean
        Dim Progress As Boolean = False
        For Each Result In ( _
                From g In Groups From c1 In g.PossibleCells Where c1.Value.Count = 2 From c2 In g.PossibleCells Where c2.Key > c1.Key AndAlso c1.Value.SequenceEqual(c2.Value) _
                From c In g.PossibleCells Where c.Value.Intersect(c1.Value).Count > 0 AndAlso Not c.Key.In(c1.Key, c2.Key) Let Cell1 = c1.Value(0), Cell2 = c1.Value(1)).ToList
            Log.Append("hidden pair detected at " & Result.Cell1 & Result.Cell2 & " for " & Result.c1.Key & Result.c2.Key & " eliminating other values")
            Result.Cell1.Eliminate(Result.c.Key)
            Result.Cell2.Eliminate(Result.c.Key)
            Progress = True
        Next
        Return Progress
    End Function

    Function HiddenTriplets() As Boolean
        Dim Progress As Boolean = False
        For Each Result In ( _
                From g In Groups From c1 In g.PossibleCells Where c1.Value.Count.Between(2, 3) _
                From c2 In g.PossibleCells Where c2.Value.Count.Between(2, 3) AndAlso c2.Key > c1.Key Let J12 = c1.Value.Union(c2.Value) Where J12.Count = 3 _
                From c3 In g.PossibleCells Where c3.Value.Count.Between(2, 3) AndAlso c3.Key > c2.Key Let J123 = c3.Value.Union(J12) Where J123.Count = 3 _
                From c In g.PossibleCells Where c.Value.Intersect(J123).Count > 0 AndAlso Not c.Key.In(c1.Key, c2.Key, c3.Key) Let Cell1 = J123(0), Cell2 = J123(1), Cell3 = J123(2)).ToList
            Log.Append("hidden triplet detected at " & Result.Cell1 & Result.Cell2 & Result.Cell3 & " for " & Result.c1.Key & Result.c2.Key & Result.c3.Key & " eliminating other values")
            Result.Cell1.Eliminate(Result.c.Key)
            Result.Cell2.Eliminate(Result.c.Key)
            Result.Cell3.Eliminate(Result.c.Key)
            Progress = True
        Next
    End Function

    Function XWing() As Boolean
        Dim Progress As Boolean = False
        For Each Result In ( _
                From g1 In Groups Let x = g1.Type, y = 3 - x Where x < 3 From c1 In g1.PossibleCells Where c1.Value.Count = 2 _
                From g2 In Groups Where g2 > g1 From c2 In g2.PossibleCells Where c2.Value.Count = 2 AndAlso c1.Key = c2.Key _
                Let d1 = (From c In c1.Value.Union(c2.Value) Select c.Index(y)).Distinct Where d1.Count = 2 _
                Let d2 = (From c In c1.Value.Union(c2.Value) Select c.Index(x)).Distinct Where d2.Count = 2 _
                From Cell In Cells Where Cell(c1.Key) AndAlso d1.Contains(Cell.Index(y)) AndAlso Not d2.Contains(Cell.Index(x))).ToList
            Progress = True
            Log.Append("x-wing detected at " & Result.c1.Value(0) & Result.c1.Value(1) & Result.c2.Value(0) & Result.c2.Value(1) & " eliminating " & Result.c1.Key & " at " & Result.Cell)
            Result.Cell.Eliminate(Result.c1.Key)
        Next
        Return Progress
    End Function

    Function SwordFish() As Boolean
        Dim Progress As Boolean = False
        For Each Result In ( _
                From g1 In Groups Let x = g1.Type, y = 3 - x Where x < 3 From c1 In g1.PossibleCells Where c1.Value.Count.Between(2, 3) _
                From g2 In Groups Where g2 > g1 From c2 In g2.PossibleCells Where c2.Value.Count.Between(2, 3) AndAlso c1.Key = c2.Key Let int = (From c In c1.Value.Union(c2.Value) Select c.Index(y)).Distinct Where int.Count = 3 _
                From g3 In Groups Where g3 > g2 From c3 In g3.PossibleCells Where c3.Value.Count.Between(2, 3) AndAlso c2.Key = c3.Key Let d1 = (From c In c3.Value Select c.Index(y)).Union(int).Distinct Where d1.Count = 3 _
                Let d2 = (From c In c1.Value.Union(c2.Value).Union(c3.Value) Select c.Index(x)).Distinct Where d2.Count = 3 _
                From Cell In Cells Where Cell(c1.Key) AndAlso d1.Contains(Cell.Index(y)) AndAlso Not d2.Contains(Cell.Index(x))).ToList
            Log.Append("sword-fish detected")
            Result.Cell.Eliminate(Result.c1.Key)
            Progress = True
        Next
        Return Progress
    End Function

    Function JellyFish() As Boolean
        Dim Progress As Boolean = False
        For Each Result In ( _
                From g1 In Groups Let x = g1.Type, y = 3 - x Where x < 3 From c1 In g1.PossibleCells Where c1.Value.Count.Between(2, 4) _
                From g2 In Groups Where g2 > g1 From c2 In g2.PossibleCells Where c2.Value.Count.Between(2, 4) AndAlso c1.Key = c2.Key Let int1 = (From c In c1.Value.Union(c2.Value) Select c.Index(y)).Distinct Where int1.Count.Between(2, 3) _
                From g3 In Groups Where g3 > g2 From c3 In g3.PossibleCells Where c3.Value.Count.Between(2, 4) AndAlso c2.Key = c3.Key Let int2 = (From c In c3.Value Select c.Index(y)).Union(int1).Distinct Where int2.Count.Between(3, 4) _
                From g4 In Groups Where g4 > g3 From c4 In g4.PossibleCells Where c4.Value.Count.Between(2, 4) AndAlso c3.Key = c4.Key Let d1 = (From c In c4.Value Select c.Index(y)).Union(int2).Distinct Where d1.Count = 4 _
                Let d2 = (From c In c1.Value.Union(c2.Value).Union(c3.Value).Union(c4.Value) Select c.Index(x)).Distinct Where d2.Count = 4 _
                From Cell In Cells Where Cell(c1.Key) AndAlso d1.Contains(Cell.Index(y)) AndAlso Not d2.Contains(Cell.Index(x))).ToList
            Log.Append("jelly-fish detected")
            Result.Cell.Eliminate(Result.c1.Key)
            Progress = True
        Next
        Return Progress
    End Function

End Class
